/**
 * Project of Vehicle Routing Problem with Time Windows implements 
 * some of well-known algorithms for solving VRPTW and presents new 
 * one.
 *
 * @author Michal Drobny  
 */
package logic.restriction.capacity;

import logic.car.VehicleProvider;
import logic.destination.Destination;
import logic.restriction.enumeration.ChangeCycleVariant;
import logic.restriction.enumeration.IterationType;
import logic.restriction.interfaces.HasCapacity;
import logic.route.Cycle;
import logic.route.Route;

/**
 * Represents the integer restriction for capacity. The current type is integer.
 * All restriction is stored in object RestrictionAdapter.
 * 
 * @author Michal Drobny
 * @date 25.11.2014
 */
public class IntegerCapacityRestriction extends CapacityRestriction<Integer> {

	private Integer currentCapacity = 0;

	/**
	 * Constructor.
	 * 
	 * @param capacity The capacity
	 */
	public IntegerCapacityRestriction(Integer capacity) {
		super(capacity);
	}

	@Override
	public void iterate(IterationType type, Object... params) {
		switch (type) {
			case DESTINATION_ONLY:
				if (params.length == 2 && params[1] instanceof HasCapacity<?> 
				&& ((HasCapacity<?>) params[1]).getCapacity() instanceof Integer) {
						Integer destinationCapacity = (Integer) ((HasCapacity<?>) params[1]).getCapacity();
						if (destinationCapacity != null && currentCapacity + destinationCapacity <= capacity)
							currentCapacity += destinationCapacity;
				}
				break;
				
			case DESTINATION_TO_START:
				if (params.length == 3 && params[1] instanceof HasCapacity<?> 
				&& ((HasCapacity<?>) params[1]).getCapacity() instanceof Integer) {
						Integer destinationCapacity = (Integer) ((HasCapacity<?>) params[1]).getCapacity();
						if (destinationCapacity != null && currentCapacity + destinationCapacity <= capacity)
							currentCapacity += destinationCapacity;
				}
				break;
		
			case ROUTE:
				// Do nothing, should be used canIterate method  
				break;
				
			case CYCLE:
				// Do nothing, should be used canIterate method
				break;
		}
		
	}

	@Override
	public boolean canIterate(IterationType type, Object... params) {
		switch (type) {
			case DESTINATION_ONLY:
				if (params.length == 2 && params[1] instanceof HasCapacity<?> 
				&& ((HasCapacity<?>) params[1]).getCapacity() instanceof Integer) {
						Integer destinationCapacity = (Integer) ((HasCapacity<?>) params[1]).getCapacity();
						if (destinationCapacity != null && currentCapacity + destinationCapacity <= capacity)
							return true;
				}
				return false;
				
			case DESTINATION_TO_START:
				if (params.length == 3 && params[1] instanceof HasCapacity<?> 
				&& ((HasCapacity<?>) params[1]).getCapacity() instanceof Integer) {
						Integer destinationCapacity = (Integer) ((HasCapacity<?>) params[1]).getCapacity();
						if (destinationCapacity != null && currentCapacity + destinationCapacity <= capacity)
							return true;
				}
				return false;
			
			case ROUTE:
				if (params.length == 3 && params[0] instanceof Route && params[1] instanceof VehicleProvider && params[2] instanceof Destination) {
					Route route = (Route) params[0];
					VehicleProvider vehicleProvider = (VehicleProvider) params[1];
					Destination start = (Destination) params[2];
					Integer sumCapacity = 0;
					if (route != null && route.getDestinations() != null && start != null) {
						Destination[] destinations = route.getOrderedDestinations(start);
						if (destinations == null)
							return false;
						for (int i = 1; i < destinations.length; ++i) {
							Destination destination = destinations[i];
							if (destination.equals(start)) {
								if (canNewCycle(ChangeCycleVariant.VEHICLE_PROVIDER, vehicleProvider)) {
									newCycle(ChangeCycleVariant.VEHICLE_PROVIDER, vehicleProvider);
									sumCapacity = 0;
								} else
									return false;
							}
							if (destination instanceof HasCapacity<?> && 
									((HasCapacity<?>) destination).getCapacity() instanceof Integer) {
								sumCapacity += (Integer) ((HasCapacity<?>) destination).getCapacity();
								if (sumCapacity > getCapacity())
									return false;
							} else
								return false;
						}
						
						if (vehicleProvider != null)
							vehicleProvider.reset();
					} else
						return false;
				} else
					return false;
				
				return true;
			
			case CYCLE:
				if (params.length == 1 && params[0] instanceof Cycle) {
					Cycle cycle = (Cycle) params[0];
					Integer sumCapacity = 0;
					if (cycle.getDestinations() != null) {
						for (Destination destination : cycle.getDestinations()) {
							if (destination instanceof HasCapacity<?> && 
									((HasCapacity<?>) destination).getCapacity() instanceof Integer) {
								sumCapacity += (Integer) ((HasCapacity<?>) destination).getCapacity();
							}
						}
					}
					
					if (currentCapacity + sumCapacity <= capacity)
						return true;
				}
				return false;
				
			default:
				return false;
		}
	}

	@Override
	public void newCycle(ChangeCycleVariant type, Object... params) {
		reset();
		switch (type) {
			case VEHICLE_PROVIDER:
				if (params.length == 1 && params[0] instanceof VehicleProvider) {
					VehicleProvider vp = (VehicleProvider) params[0];
					if (vp.nextVehicle(true))
						if (vp.getCurrentVehicle() instanceof HasCapacity<?> && 
								((HasCapacity<?>) vp.getCurrentVehicle()).getCapacity() instanceof Integer)
							this.capacity = (Integer) ((HasCapacity<?>) vp.getCurrentVehicle()).getCapacity();
				}
				break;
		}
	}
	
	@Override
	public boolean canNewCycle(ChangeCycleVariant type, Object... params) {
		switch (type) {
			case VEHICLE_PROVIDER:
				if (params.length == 1 && params[0] instanceof VehicleProvider) {
					VehicleProvider vp = (VehicleProvider) params[0];
					if (vp.nextVehicle(false))
						if (vp.getCurrentVehicle() instanceof HasCapacity<?> && 
							((HasCapacity<?>) vp.getCurrentVehicle()).getCapacity() instanceof Integer)
							return true;
				}
				break;
		}
		return false;
	}

	@Override
	public void reset() {
		this.currentCapacity = 0;
	}
}
